/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../xos/head.h"
#include "../os/head.h"
#include "../../../../../xosbin/lib/include/iconv/head.h"
#include "../../../../../xosbin/lib/include/uuid/head.h"
#include "misc_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static misc_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    misc_impl::misc_impl()
    {
    }

    misc_impl::~misc_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int misc_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int misc_impl::term()
    {
        int nRet = 0;
        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf16_to_wchar( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;
        memcpy( lpszDst, lpszSrc, tSrcSize );
        DstSize = tSrcSize;
        return ret;
    }

    int misc_impl::wchar_to_utf16( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;
        memcpy( lpszDst, lpszSrc, tSrcSize );
        DstSize = tSrcSize;
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf8_to_wchar( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        wchar_t * out_buf = (wchar_t *)lpszDst;
        int out_buf_len = (int)(tDstBufSize / 2);
        size_t out_len = 0;

        const char * in_buf = lpszSrc;
        int in_len = (int)tSrcSize;

        if( 0 == ret )
        {
            out_len = MultiByteToWideChar( CP_UTF8, 0, in_buf, in_len, out_buf, out_buf_len );
            if( 0 == out_len )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            DstSize = out_len * 2;
            out_buf[out_len] = 0;
        }

        return ret;
    }

    int misc_impl::wchar_to_utf8( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        char * out_buf = lpszDst;
        int out_buf_len = (int)tDstBufSize;
        size_t out_len = 0;

        const wchar_t * in_buf = (const wchar_t *)lpszSrc;
        int in_len = (int)( tSrcSize / 2);

        if( 0 == ret )
        {
            out_len = WideCharToMultiByte( CP_UTF8, 0, in_buf, in_len, out_buf, out_buf_len, 0, 0 );
            if( 0 == out_len )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            DstSize = out_len;
            out_buf[out_len] = 0;
        }

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf8_to_utf16( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;
        ret = utf8_to_wchar( lpszSrc, tSrcSize, lpszDst, tDstBufSize, DstSize );
        return ret;
    }

    int misc_impl::utf16_to_utf8( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;
        ret = wchar_to_utf8( lpszSrc, tSrcSize, lpszDst, tDstBufSize, DstSize );
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // utf8 to utf16
    // 
    int misc_impl::utf8_to_utf16( const xos_box::i_string * pSrc, xos_box::i_buf * pDstBuf )
    {
        char * out_buf = pDstBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pDstBuf->get_buf_size(0);
        size_t out_size = 0;

        const char * in_buf = pSrc->get( 0, 0 );
        size_t in_size = pSrc->length();

        int ret = 0;

        if( 0 == ret )
        {
            ret = utf8_to_utf16( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            pDstBuf->set_len( (int)out_size );
        }

        return ret;
    }

    // 
    // utf16 utf8
    // 
    int misc_impl::utf16_to_utf8( xos_box::i_buf * pSrcBuf, xos_box::i_string * pDst )
    {
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = (const char *)pSrcBuf->get_data( 0, 0, 0, 0 );
        size_t in_size = pSrcBuf->get_len( 0 );

        int ret = 0;

        if( 0 == ret )
        {
            ret = utf16_to_utf8( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size;
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // utf8 to wchar
    // 
    int misc_impl::utf8_to_wchar( const xos_box::i_string * pSrc, xos_box::i_wstring * pDst )
    {
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        wchar_t * lpszResult = (wchar_t *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = pSrc->get(0,0);
        size_t in_size = pSrc->length();

        int ret = 0;

        if( 0 == ret )
        {
            ret = utf8_to_wchar( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size / 2;
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // wcharto utf8
    // 
    int misc_impl::wchar_to_utf8( const xos_box::i_wstring * pSrc, xos_box::i_string * pDst )
    {
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = (const char *)pSrc->get( 0, 0 );
        size_t in_size = pSrc->length() * 2;

        int ret = 0;

        if( 0 == ret )
        {
            ret = wchar_to_utf8( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size;
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get process full file name
    // 
    int misc_impl::get_process_file_full_name( char * pszFile, int nLen, int * pnLenNeeded )
    {
        int nRet = 0;

        char szFile[4096] = { 0 };

        if( ( 0 == nRet ) && !pszFile )
        {
            nRet = 1;
        }

        if( 0 == nRet )
        {
            ::GetModuleFileNameA( 0, szFile, sizeof( szFile ) / sizeof( szFile[0] ) );
        }

        if( 0 == nRet )
        {
            strcpy_s( pszFile, nLen, szFile );
            int len = (int)strlen( szFile );
            if( pnLenNeeded )
            {
                *pnLenNeeded = len;
            }
        }

        return nRet;
    }

    int misc_impl::get_process_file_full_name( xos_box::i_string * pStr )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size( 0 );
        {
            ::GetModuleFileNameA( 0, pB, nSize );
            pStr->set( pB );
        }
        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get process file path. including '\\'
    //
    int misc_impl::get_process_path( char * pszPath, int nLen, int * pnLenNeeded )
    {
        int nRet = 0;

        char szFile[4096] = { 0 };

        ::GetModuleFileNameA( 0, szFile, sizeof( szFile ) / sizeof( szFile[0] ) );

        {
            char* p = 0;
            p = StrRChrA( szFile, 0, '\\' );
            if( p )
            {
                *p++ = 0;
            }
        }

        if( pszPath )
        {
            strcpy_s( pszPath, nLen, szFile );
            int len = (int)strlen( szFile );
            if( pnLenNeeded )
            {
                *pnLenNeeded = len;
            }
        }
        else
        {
            nRet = 1;
        }

        return nRet;
    }

    // 
    // get process file path. including '\\'
    //
    int misc_impl::get_process_path( xos_box::i_string * pStr )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size( 0 );

        {
            ::GetModuleFileNameA( 0, pB, nSize );
        }
        {
            char * p = 0;
            p = StrRChrA( pB, 0, '\\' );
            if( p )
            {
                *p++ = 0;
            }
        }
        {
            pStr->set( pB );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // append path in all platform
    // 
    int misc_impl::path_append( char * lpszPath, int nBufSize, const char * lpszAdder )
    {
        return mgr::container()->str_append( lpszPath, nBufSize, lpszAdder, '\\' );
    }

    int misc_impl::path_append( xos_box::i_string * pStr, xos_box::i_string * pAdder )
    {
        int ret = 0;
        pStr->append( pAdder, '\\' );
        return ret;
    }

    int misc_impl::path_append( xos_box::i_string * pStr, const char * lpszAdder )
    {
        int ret = 0;
        pStr->append( lpszAdder, '\\');
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get real path.
    // 
    int misc_impl::real_path( const char * lpszPath, char * lpszRealPath, int nSize )
    {
        int ret = 0;
        BOOL bRet = PathCanonicalizeA( lpszRealPath, lpszPath );
        if(!bRet)
        {
            ret = 1;
        }
        return ret;
    }

    int misc_impl::real_path( xos_box::i_string * pPath )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        {
            BOOL bRet = PathCanonicalizeA( pB, pPath->get( 0, 0 ) );
            if( !bRet )
            {
                ret = 1;
            }
        }
        if( 0 == ret )
        {
            pPath->set( pB );
        }
        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // create guid
    // 
    int misc_impl::create_guid( char * lpszGuid )
    {
        int nRet = 0;

        if( lpszGuid )
        {
            GUID guid = { 0 };
            CoCreateGuid( &guid );
            //CComBSTR bstr( guid );
            //lstrcpyA( lpszGuid, W2CA( bstr.m_str ) );
            std::string str = GuidToString( guid );
            lstrcpyA( lpszGuid, str.c_str() );
        }

        return nRet;
    }

    // 
    // sleep 1 / 1000 second
    // 
    int misc_impl::sleep( int nMilliseconds )
    {
        int nRet = 0;
        Sleep( nMilliseconds );
        return nRet;
    }

    // 
    // output debuf info
    // 
    int misc_impl::out_put_debug_string( const char * lpszInfo )
    {
        int nRet = 0;
#ifdef _DEBUG
        OutputDebugStringA( lpszInfo );
#endif
        return nRet;
    }

    int misc_impl::out_put_to_cmd( const char * lpszInfo )
    {
        using namespace std;
        int nRet = 0;
        cout << lpszInfo << "\r\n";
        return nRet;
    }

    int misc_impl::trace( const char * lpszFormat, ... )
    {
        using namespace std;

        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size(0);

        {
            va_list  va;
            va_start( va, lpszFormat );
            ret = vsnprintf_s( pB, nSize, _TRUNCATE, lpszFormat, va );
            va_end( va );
        }
        {
            OutputDebugStringA( pB );
            cout << pB << endl;
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // get seconds from since 1970-1-1
    // 
    // 
    // get seconds from since 1970-1-1
    // 
    uint64_t misc_impl::get_wall_second( uint64_t * lpWallSecond )
    {
        uint64_t xRet = ( uint64_t )time( 0 );
        *lpWallSecond = xRet;
        return xRet;
    }

    // 
    // get process ID
    // 
    uint64_t misc_impl::get_current_process_id()
    {
        uint64_t xRet = GetCurrentProcessId();
        return xRet;
    }

    // 
    // get thread ID
    // 
    uint64_t misc_impl::get_current_thread_id()
    {
        uint64_t xRet = GetCurrentThreadId();
        return xRet;
    }

    const char * misc_impl::platform()
    {
        return os::platform();
    }

    bool misc_impl::can_hot_replace()
    {
        return os::can_hot_replace();
    }

    // 
    // get cpu num
    // 
    int misc_impl::get_cpu_num()
    {
        int nRet = 1;

        SYSTEM_INFO info = { {0} };

        GetSystemInfo( &info );

        if( info.dwNumberOfProcessors > 0 )
        {
            nRet = info.dwNumberOfProcessors;
        }

        return nRet;
    }

    // 
    // ensure_memory_visibility
    // 
    int misc_impl::ensure_memory_visibility()
    {
        int ret = 0;
#ifdef __aarch64__
        asm volatile("dsb sy" ::: "memory"); // ARM数据同步屏障
#endif
        return ret;
    }

    // 
    // aligned alloc & free
    // 
    void* misc_impl::aligned_alloc( size_t size, size_t alignment )
    {
        void* pRet = 0;
        xos_stl::lock_free::aligned_alloc_and_free obj;
        pRet = obj.aligned_alloc( size, alignment );
        //pRet = _aligned_malloc( size, alignment );
        return pRet;
    }

    void misc_impl::aligned_free( void* ptr )
    {
        xos_stl::lock_free::aligned_alloc_and_free obj;
        obj.aligned_free( ptr );
        //_aligned_free( ptr );
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    std::string misc_impl::GuidToString( const GUID & guid )
    {
        std::ostringstream oss;
        oss << std::hex << std::setfill( '0' )
            << std::setw( 8 ) << guid.Data1 << "-"
            << std::setw( 4 ) << guid.Data2 << "-"
            << std::setw( 4 ) << guid.Data3 << "-"
            << std::setw( 2 ) << static_cast<int>( guid.Data4[0] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[1] ) << "-"
            << std::setw( 2 ) << static_cast<int>( guid.Data4[2] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[3] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[4] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[5] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[6] )
            << std::setw( 2 ) << static_cast<int>( guid.Data4[7] );
        return oss.str();
    }

} // xos
